import 'package:dio/dio.dart';

import '../configs/app_config.dart';
import '../api/auth.dart' as api;

import '../utils/http.dart';
import '../utils/local_storage.dart';

import '../models/login_request.dart';
import '../models/login_response.dart';
import '../models/user_session.dart';

class AuthService {
  factory AuthService() => _getInstance();
  static AuthService get instance => _getInstance();
  static AuthService _instance;
  static AuthService _getInstance() {
    if (_instance == null) {
      _instance = AuthService._internal();
    }
    return _instance;
  }

  UserSession _session;

  AuthService._internal() {
    // nothing
  }

  Future<bool> login(String username, String password) async {
    LoginResponse res = await api.login(LoginRequest(
      username: username,
      password: password,
    ));
    await LocalStorage.instance.setJsonObject(
      AppConfig.sessionKey,
      _session = UserSession.fromLoginResponse(res),
    );
    await LocalStorage.instance.setString(
      AppConfig.accessTokenKey,
      _session.accessToken,
    );

    return true;
  }

  Future<bool> isLogined() async {
    try {
      if (_session == null) {
        final dynamic json =
            await LocalStorage.instance.getJsonObject(AppConfig.sessionKey);
        _session = UserSession.fromJson(json);
      }

      if (_session != null) {
        return _session.isExpired();
      }
    } catch (ex) {
      await this.logout();
    }

    return false;
  }

  Future<void> logout() async {
    _session = null;
    await LocalStorage.instance.removeByKey(AppConfig.sessionKey);
    await LocalStorage.instance.removeByKey(AppConfig.accessTokenKey);
  }

  Future<void> interceptors() {
    Dio _dio = HttpUtil.instance.getDio();
    _dio.interceptors.add(_AuthInterceptor());
    _dio.interceptors.add(_TokenInterceptor());
    return Future.value();
  }

  Future<void> refreshToken() async {}
}

class _AuthInterceptor extends Interceptor {
  @override
  onRequest(RequestOptions options) async {
    String accessToken =
        await LocalStorage.instance.getString(AppConfig.accessTokenKey);
    if (accessToken != null && accessToken.isNotEmpty) {
      options.headers['Authorization'] = 'Bearer $accessToken';
    }

    return super.onRequest(options);
  }
}

class _TokenInterceptor extends Interceptor {
  @override
  onError(DioError error) async {
    //401代表token过期
    if (error.response != null && error.response.statusCode == 401) {
      var dio = HttpUtil.instance.getDio();
      var request = error.response.request;

      dio.lock();

      if (!request.extra.containsKey(AppConfig.retriedKey)) {
        request.extra[AppConfig.retriedKey] = 0;
      }

      int retried = request.extra[AppConfig.retriedKey];
      if (retried < 1) {
        request.extra[AppConfig.retriedKey] = ++retried;
        await AuthService.instance.logout();
        await AuthService.instance.refreshToken();
      }

      dio.unlock();

      // 重新请求失败接口
      if (await AuthService.instance.isLogined()) {
        try {
          var response = await dio.request(
            request.path,
            data: request.data,
            queryParameters: request.queryParameters,
            cancelToken: request.cancelToken,
            options: request,
            onReceiveProgress: request.onReceiveProgress,
          );
          return response;
        } on DioError catch (e) {
          return super.onError(e);
        }
      }
    }

    return super.onError(error);
  }
}
